/*!
\copyright  Copyright (c) 2025 Qualcomm Technologies International, Ltd.
            All Rights Reserved.
            Qualcomm Technologies International, Ltd. Confidential and Proprietary.
\file
\brief      Handles updates to streaming mode, core aspect of Streaming Mode Manager module

*/

#include "streaming_mode_manager_core.h"
#include "bandwidth_manager.h"
#include "mirror_profile.h"
#include "bt_device.h"
#include "av_instance.h"
#include "a2dp_profile.h"
#include "qualcomm_connection_manager.h"
#include "kymera_latency_manager.h"
#include <logging.h>
#include <panic.h>

typedef enum
{
    RETRY_QCM_MODE_CHANGED,
} internal_message_ids;

typedef struct
{
    uint8 new_mode;
    uint8 num_of_attempts;
} RETRY_QCM_MODE_CHANGED_T;

static void streamingModeManagerCore_MessageHandler(Task task, MessageId id, Message message);
static const TaskData task_data = {.handler = streamingModeManagerCore_MessageHandler};
#define TASK ((Task)&task_data)

#ifdef INCLUDE_QCOM_CON_MANAGER

static bool streamingModeManagerCore_SetQcmMode(const bdaddr *bd_addr, uint8 mode)
{
    device_t device = BtDevice_GetDeviceForBdAddr(bd_addr);
    tp_bdaddr tp_addr;
    bool status = FALSE;
    if (BtDevice_GetTpBdaddrForDevice(device, &tp_addr))
    {
        QcomConManagerSetStreamingMode(&tp_addr, mode);
        status = TRUE;
    }
    return status;
}

static bool streamingModeManagerCore_SetQcmModeForEachA2dpConnectedHandset(uint8 mode)
{
    avInstanceTaskData* theInst;
    av_instance_iterator_t iterator;
    bool status = FALSE;
    for_all_av_instances(theInst, &iterator)
    {
        if (theInst && appA2dpIsSinkCodec(theInst) && appA2dpIsConnectedMedia(theInst))
        {
            status |= streamingModeManagerCore_SetQcmMode(&theInst->bd_addr, mode);
        }
    }
    return status;
}

static bool streamingModeManagerCore_SetQcmModeForMirroredHandset(uint8 mode)
{
    const bdaddr *mirror_addr = MirrorProfile_GetMirroredDeviceAddress();
    bool status = FALSE;
    if (mirror_addr)
    {
        status |= streamingModeManagerCore_SetQcmMode(mirror_addr, mode);
    }
    return status;
}

static bool streamingModeManagerCore_QcmModeChanged(uint8 new_mode)
{
    bool status = FALSE;
    if(BtDevice_IsMyAddressPrimary())
    {
        status |= streamingModeManagerCore_SetQcmModeForEachA2dpConnectedHandset(new_mode);
    }
    else
    {
        status |= streamingModeManagerCore_SetQcmModeForMirroredHandset(new_mode);
    }
    return status;
}

#else // INCLUDE_QCOM_CON_MANAGER

static bool streamingModeManagerCore_QcmModeChanged(uint8 new_mode)
{
    UNUSED(new_mode);
    return TRUE;
}

#endif // INCLUDE_QCOM_CON_MANAGER

static void streamingModeManagerCore_HandleQcmModeChangedAttempts(uint8 new_mode, uint8 current_number_of_attempts)
{
    DEBUG_LOG_DEBUG("streamingModeManagerCore_HandleQcmModeChangedAttempts(%d, %d)", new_mode, current_number_of_attempts);
    MessageCancelAll(TASK, RETRY_QCM_MODE_CHANGED);
    // Try for a few times/time, at which point give up since the source is most likely already disconnected from the profile
    if ((streamingModeManagerCore_QcmModeChanged(new_mode) == FALSE) && (current_number_of_attempts < 10))
    {
        RETRY_QCM_MODE_CHANGED_T *msg = PanicUnlessMalloc(sizeof(*msg));
        msg->new_mode = new_mode;
        msg->num_of_attempts = current_number_of_attempts + 1;
        MessageSendLater(TASK, RETRY_QCM_MODE_CHANGED, msg, 50);
    }
}

static void streamingModeManagerCore_MessageHandler(Task task, MessageId id, Message message)
{
    UNUSED(task);
    switch(id)
    {
        case RETRY_QCM_MODE_CHANGED:
        {
            const RETRY_QCM_MODE_CHANGED_T *msg = message;
            streamingModeManagerCore_HandleQcmModeChangedAttempts(msg->new_mode, msg->num_of_attempts);
        }
        break;
        default:
        break;
    }
}

void StreamingModeManagerCore_Init(void)
{
    PanicFalse(BandwidthManager_RegisterFeature(BANDWIDTH_MGR_FEATURE_A2DP_LL, high_bandwidth_manager_priority, NULL));
    PanicFalse(BandwidthManager_RegisterFeature(BANDWIDTH_MGR_FEATURE_A2DP_HIGH_BW, high_bandwidth_manager_priority, NULL));
}

void StreamingModeManagerCore_QcmModeChanged(uint8 new_mode)
{
    DEBUG_LOG_STATE("StreamingModeManagerCore_QcmModeChanged(%d)", new_mode);
    streamingModeManagerCore_HandleQcmModeChangedAttempts(new_mode, 0);
}

void StreamingModeManagerCore_A2dpLlStateChanged(uint8 new_state)
{
    DEBUG_LOG_STATE("StreamingModeManagerCore_A2dpLlStateChanged(%d)", new_state);
    if (new_state != LOW_LATENCY_STREAM_INACTIVE)
    {
        BandwidthManager_FeatureStart(BANDWIDTH_MGR_FEATURE_A2DP_LL);
    }
    else if (new_state == LOW_LATENCY_STREAM_INACTIVE)
    {
        BandwidthManager_FeatureStop(BANDWIDTH_MGR_FEATURE_A2DP_LL);
    }
}

void StreamingModeManagerCore_A2dpHbStateChanged(uint8 new_state)
{
    DEBUG_LOG_STATE("StreamingModeManagerCore_A2dpHbStateChanged(%d)", new_state);
    if (new_state == HIGH_BANDWIDTH_STREAM_ACTIVE)
    {
        BandwidthManager_FeatureStart(BANDWIDTH_MGR_FEATURE_A2DP_HIGH_BW);
    }
    else if(new_state == HIGH_BANDWIDTH_STREAM_INACTIVE)
    {
        BandwidthManager_FeatureStop(BANDWIDTH_MGR_FEATURE_A2DP_HIGH_BW);
    }
}
